package com.dimenlink.service.impl;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import com.dimenlink.component.RedisComponent;
import com.dimenlink.entity.config.AppConfig;
import com.dimenlink.entity.constants.Constants;
import com.dimenlink.entity.dto.SysSettingDto;
import com.dimenlink.entity.dto.UploadingFileDto;
import com.dimenlink.entity.enums.*;
import com.dimenlink.entity.po.VideoInfo;
import com.dimenlink.entity.po.VideoInfoFile;
import com.dimenlink.entity.po.VideoInfoFilePost;
import com.dimenlink.entity.query.*;
import com.dimenlink.exception.BusinessException;
import com.dimenlink.mappers.VideoInfoFileMapper;
import com.dimenlink.mappers.VideoInfoFilePostMapper;
import com.dimenlink.mappers.VideoInfoMapper;
import com.dimenlink.utils.CopyTools;
import com.dimenlink.utils.FFmpegUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Service;

import com.dimenlink.entity.po.VideoInfoPost;
import com.dimenlink.entity.vo.PaginationResultVO;
import com.dimenlink.mappers.VideoInfoPostMapper;
import com.dimenlink.service.VideoInfoPostService;
import com.dimenlink.utils.StringTools;
import org.springframework.transaction.annotation.Transactional;


/**
 * 视频信息 业务接口实现
 */
@Service("videoInfoPostService")
@Slf4j
public class VideoInfoPostServiceImpl implements VideoInfoPostService {

	@Resource
	private VideoInfoPostMapper<VideoInfoPost, VideoInfoPostQuery> videoInfoPostMapper;
	@Resource
	private VideoInfoFilePostMapper<VideoInfoFilePost, VideoInfoFilePostQuery> videoInfoFilePostMapper;

	@Resource
	private VideoInfoMapper<VideoInfo, VideoInfoQuery> videoInfoMapper;
	@Resource
	private VideoInfoFileMapper<VideoInfoFile, VideoInfoFileQuery> videoInfoFileMapper;

	@Resource
	private RedisComponent redisComponent;

	@Resource
	private AppConfig appConfig;

	@Resource
	private FFmpegUtils fFmpegUtils;

	/**
	 * 根据条件查询列表
	 */
	@Override
	public List<VideoInfoPost> findListByParam(VideoInfoPostQuery param) {
		return this.videoInfoPostMapper.selectList(param);
	}

	/**
	 * 根据条件查询列表
	 */
	@Override
	public Integer findCountByParam(VideoInfoPostQuery param) {
		return this.videoInfoPostMapper.selectCount(param);
	}

	/**
	 * 分页查询方法
	 */
	@Override
	public PaginationResultVO<VideoInfoPost> findListByPage(VideoInfoPostQuery param) {
		int count = this.findCountByParam(param);
		int pageSize = param.getPageSize() == null ? PageSize.SIZE15.getSize() : param.getPageSize();

		SimplePage page = new SimplePage(param.getPageNo(), count, pageSize);
		param.setSimplePage(page);
		List<VideoInfoPost> list = this.findListByParam(param);
		PaginationResultVO<VideoInfoPost> result = new PaginationResultVO(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), list);
		return result;
	}

	/**
	 * 新增
	 */
	@Override
	public Integer add(VideoInfoPost bean) {
		return this.videoInfoPostMapper.insert(bean);
	}

	/**
	 * 批量新增
	 */
	@Override
	public Integer addBatch(List<VideoInfoPost> listBean) {
		if (listBean == null || listBean.isEmpty()) {
			return 0;
		}
		return this.videoInfoPostMapper.insertBatch(listBean);
	}

	/**
	 * 批量新增或者修改
	 */
	@Override
	public Integer addOrUpdateBatch(List<VideoInfoPost> listBean) {
		if (listBean == null || listBean.isEmpty()) {
			return 0;
		}
		return this.videoInfoPostMapper.insertOrUpdateBatch(listBean);
	}

	/**
	 * 多条件更新
	 */
	@Override
	public Integer updateByParam(VideoInfoPost bean, VideoInfoPostQuery param) {
		StringTools.checkParam(param);
		return this.videoInfoPostMapper.updateByParam(bean, param);
	}

	/**
	 * 多条件删除
	 */
	@Override
	public Integer deleteByParam(VideoInfoPostQuery param) {
		StringTools.checkParam(param);
		return this.videoInfoPostMapper.deleteByParam(param);
	}

	/**
	 * 根据VideoId获取对象
	 */
	@Override
	public VideoInfoPost getVideoInfoPostByVideoId(String videoId) {
		return this.videoInfoPostMapper.selectByVideoId(videoId);
	}

	/**
	 * 根据VideoId修改
	 */
	@Override
	public Integer updateVideoInfoPostByVideoId(VideoInfoPost bean, String videoId) {
		return this.videoInfoPostMapper.updateByVideoId(bean, videoId);
	}

	/**
	 * 根据VideoId删除
	 */
	@Override
	public Integer deleteVideoInfoPostByVideoId(String videoId) {
		return this.videoInfoPostMapper.deleteByVideoId(videoId);
	}







	/**
	 * 保存视频信息的核心业务逻辑实现
	 *
	 * @param videoInfoPost 视频基本信息对象
	 * @param uploadFileList 上传的视频文件列表
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void saveVideoInfo(VideoInfoPost videoInfoPost, List<VideoInfoFilePost> uploadFileList) {
		// 检查上传文件分p数量是否超过系统限制,比如一个电视剧分为20集，一集一p
		if (uploadFileList.size() > redisComponent.getSysSettingDto().getVideoPCount()) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}

		// 如果视频ID不为空，说明是更新操作，需要进行权限和状态检查
		if (!StringTools.isEmpty(videoInfoPost.getVideoId())) {
			// 查询数据库中的视频信息
			VideoInfoPost videoInfoPostDb = this.videoInfoPostMapper.selectByVideoId(videoInfoPost.getVideoId());
			// 如果数据库中不存在该视频，抛出异常
			if (videoInfoPostDb == null) {
				throw new BusinessException(ResponseCodeEnum.CODE_600);
			}
			// 如果视频状态为转码中(STATUS0)或待审核(STATUS2)，不允许更新
			if (ArrayUtils.contains(new Integer[]{VideoStatusEnum.STATUS0.getStatus(), VideoStatusEnum.STATUS2.getStatus()}, videoInfoPostDb.getStatus())) {
				throw new BusinessException(ResponseCodeEnum.CODE_600);
			}
		}
		// 获取当前时间
		Date curDate = new Date();
		String videoId = videoInfoPost.getVideoId();

		// 初始化待删除和待添加的文件列表
		List<VideoInfoFilePost> deleteFileList = new ArrayList<>();
		List<VideoInfoFilePost> addFileList = uploadFileList;

		// 如果视频ID为空，说明是新增视频
		if (StringTools.isEmpty(videoId)) {
			// 生成10位随机字符串作为视频ID
			videoId = StringTools.getRandomString(Constants.LENGTH_10);
			videoInfoPost.setVideoId(videoId);

			// 设置创建时间和最后更新时间
			videoInfoPost.setCreateTime(curDate);
			videoInfoPost.setLastUpdateTime(curDate);

			// 设置初始状态为转码中
			videoInfoPost.setStatus(VideoStatusEnum.STATUS0.getStatus());
			// 插入视频基本信息到数据库
			this.videoInfoPostMapper.insert(videoInfoPost);
		} else {
			// 更新视频信息的处理逻辑

			// 查询数据库中该视频已有的文件列表
			VideoInfoFilePostQuery fileQuery = new VideoInfoFilePostQuery();
			fileQuery.setVideoId(videoId);
			fileQuery.setUserId(videoInfoPost.getUserId());
			List<VideoInfoFilePost> dbInfoFileList = this.videoInfoFilePostMapper.selectList(fileQuery);
			// 将上传的文件列表转换为以uploadId为key的Map，便于快速查找
			Map<String, VideoInfoFilePost> uploadFileMap = uploadFileList.stream().collect(Collectors.toMap(item -> item.getUploadId(), Function.identity(),
					(data1, data2) -> data2));

			// 标记是否有文件名变更
			Boolean updateFileName = false;

			// 遍历数据库中的文件列表，找出需要删除的文件和文件名变更的文件
			for (VideoInfoFilePost fileInfo : dbInfoFileList) {
				// 在上传文件中查找对应的文件
				VideoInfoFilePost updateFile = uploadFileMap.get(fileInfo.getUploadId());
				// 如果上传文件中没有该文件，说明需要删除
				if (updateFile == null) {
					deleteFileList.add(fileInfo);
				} else if (!updateFile.getFileName().equals(fileInfo.getFileName())) {
					// 如果文件名不一致，说明文件名有变更
					updateFileName = true;
				}
			}
			// 筛选出新增的文件（fileId为空的文件）
			addFileList = uploadFileList.stream().filter(item -> item.getFileId() == null).collect(Collectors.toList());

			// 设置最后更新时间
			videoInfoPost.setLastUpdateTime(curDate);

			// 检查视频基本信息是否有变更
			Boolean changeVideoInfo = this.changeVideoInfo(videoInfoPost);
			// 根据变更情况设置视频状态：
			// 1. 如果有新增文件，设置为转码中状态
			if (!addFileList.isEmpty()) {
				videoInfoPost.setStatus(VideoStatusEnum.STATUS0.getStatus());
			} else if (changeVideoInfo || updateFileName) {
				// 2. 如果视频信息或文件名有变更，设置为待审核状态
				videoInfoPost.setStatus(VideoStatusEnum.STATUS2.getStatus());
			}
			// 更新视频基本信息
			this.videoInfoPostMapper.updateByVideoId(videoInfoPost, videoInfoPost.getVideoId());
		}
		// 删除文件
		if (!deleteFileList.isEmpty()) {
			//获取删除文件ID
			List<String> delFileList = deleteFileList.stream().map(item -> item.getFileId()).collect(Collectors.toList());
			// 删除文件
			this.videoInfoFilePostMapper.deleteBatchByFileId(delFileList, videoInfoPost.getUserId());

			// 获取删除文件对应的文件路径
			List<String> delFilePathList = deleteFileList.stream().map(item -> item.getFilePath()).collect(Collectors.toList());

			// 添加文件删除任务到删除队列
			redisComponent.addFile2DelQueue(videoId, delFilePathList);
		}

		// 添加文件信息
		Integer index = 1;
		for (VideoInfoFilePost videoInfoFile : uploadFileList) {
			// 设置文件索引
			videoInfoFile.setFileIndex(index++);
			videoInfoFile.setVideoId(videoId);
			videoInfoFile.setUserId(videoInfoPost.getUserId());
			if (videoInfoFile.getFileId() == null) {
				videoInfoFile.setFileId(StringTools.getRandomString(Constants.LENGTH_20));
				videoInfoFile.setUpdateType(VideoFileUpdateTypeEnum.UPDATE.getStatus());
				videoInfoFile.setTransferResult(VideoFileTransferResultEnum.TRANSFER.getStatus());
			}
		}
		this.videoInfoFilePostMapper.insertOrUpdateBatch(uploadFileList);

		// 添加转码任务
		if (addFileList != null && !addFileList.isEmpty()) {
			for (VideoInfoFilePost file : addFileList) {
				file.setUserId(videoInfoPost.getUserId());
				file.setVideoId(videoId);
			}
			redisComponent.addFile2TransferQueue(addFileList);
		}
	}

	/**
	 * 转码文件
	 *
	 * @param videoInfoFilePost 文件信息
	 */
	@Override
	public void transferVideoFile(VideoInfoFilePost videoInfoFilePost) {
		VideoInfoFilePost updateFilePost = new VideoInfoFilePost();
		try {
			// 获取上传文件信息
			UploadingFileDto fileDto = redisComponent.getUploadVideoFile(videoInfoFilePost.getUserId(), videoInfoFilePost.getUploadId());

			// 获取上传文件路径
			String tempFilePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_FOLDER_TEMP + fileDto.getFilePath();
			// 创建临时文件对象
			File tempFile = new File(tempFilePath);

			// 创建目标文件对象
			String targetFilePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_VIDEO + fileDto.getFilePath();
			File targetFile = new File(targetFilePath);

			//位于临时目录（FILE_FOLDER_TEMP）中的文件复制到视频目录（FILE_VIDEO）
			FileUtils.copyDirectory(tempFile, targetFile);

			//删除临时目录
			FileUtils.forceDelete(tempFile);

			redisComponent.delVideoFileInfo(videoInfoFilePost.getUserId(), videoInfoFilePost.getUploadId());

			//合并文件
			String completeVideo = targetFilePath + Constants.TEMP_VIDEO_NAME;
			this.union(targetFilePath, completeVideo, true);

			// 获取播放时长
			Integer duration = fFmpegUtils.getVideoInfoDuration(completeVideo);
			// 将获取到的视频时长，设置到 updateFilePost 对象中（updateFilePost 应该是封装视频信息的实体类）
			updateFilePost.setDuration(duration);

			// 将文件大小设置到 updateFilePost 对象中
			updateFilePost.setFileSize(new File(completeVideo).length());

			// 将拼接后的文件路径设置到 updateFilePost 对象中
			updateFilePost.setFilePath(Constants.FILE_VIDEO + fileDto.getFilePath());

			// 将转换成功状态设置到 updateFilePost 对象中
			updateFilePost.setTransferResult(VideoFileTransferResultEnum.SUCCESS.getStatus());

			//调用当前类的 convertVideo2Ts 方法，传入完整视频路径，执行视频转 TS 格式及相关切片、生成索引等操作
			this.convertVideo2Ts(completeVideo);
		} catch (Exception e) {
			log.error("文件转码失败", e);
			updateFilePost.setTransferResult(VideoFileTransferResultEnum.FAIL.getStatus());
		} finally {

			// 更新视频文件记录：根据 uploadId 和 userId，用 updateFilePost 的数据更新数据库
			videoInfoFilePostMapper.updateByUploadIdAndUserId(
					updateFilePost,
					videoInfoFilePost.getUploadId(),
					videoInfoFilePost.getUserId()
			);

			// 构建查询条件：查询同视频下的转码失败记录
			VideoInfoFilePostQuery filePostQuery = new VideoInfoFilePostQuery();
			filePostQuery.setVideoId(videoInfoFilePost.getVideoId()); // 设置视频 ID
			filePostQuery.setTransferResult(
					VideoFileTransferResultEnum.FAIL.getStatus() // 查询“转码失败”的记录
			);

			// 统计转码失败的数量
			Integer failCount = videoInfoFilePostMapper.selectCount(filePostQuery);

			// 如果有失败记录，标记视频为【失败状态】并提前返回
			if (failCount > 0) {
				VideoInfoPost videoUpdate = new VideoInfoPost();
				videoUpdate.setStatus(
						VideoStatusEnum.STATUS1.getStatus() // 设置视频状态为“失败/异常”
				);
				// 根据 videoId 更新视频主表状态
				videoInfoPostMapper.updateByVideoId(
						videoUpdate,
						videoInfoFilePost.getVideoId()
				);
				return; // 提前结束，不执行后续逻辑
			}

			// 重新设置查询条件：查询同视频下“转码中”的记录
			filePostQuery.setTransferResult(
					VideoFileTransferResultEnum.TRANSFER.getStatus() // 查询“转码中”的记录
			);

			// 统计“转码中”的数量
			Integer transferCount = videoInfoFilePostMapper.selectCount(filePostQuery);

			// 如果没有“转码中”的记录，说明所有分p已处理完成
			if (transferCount == 0) {
				// 统计该视频下所有分p的总时长
				Integer duration = videoInfoFilePostMapper.sumDuration(
						videoInfoFilePost.getVideoId()
				);

				VideoInfoPost videoUpdate = new VideoInfoPost();
				videoUpdate.setStatus(
						VideoStatusEnum.STATUS2.getStatus() // 设置视频状态为“转码完成”
				);
				videoUpdate.setDuration(duration); // 设置视频总时长

				// 根据 videoId 更新视频主表的状态和时长
				videoInfoPostMapper.updateByVideoId(
						videoUpdate,
						videoInfoFilePost.getVideoId()
				);
			}
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void auditVideo(String videoId, Integer status, String reason) {
		// 根据传入的状态值获取对应的视频状态枚举对象
		VideoStatusEnum videoStatusEnum = VideoStatusEnum.getByStatus(status);
		// 如果枚举对象为null，说明传入的状态值不合法，抛出业务异常
		if (videoStatusEnum == null) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}

		// 创建VideoInfoPost对象，用于设置更新的视频状态等信息
		VideoInfoPost videoInfoPost = new VideoInfoPost();
		videoInfoPost.setStatus(status);

		// 创建VideoInfoPostQuery对象，用于设置查询条件
		VideoInfoPostQuery videoInfoPostQuery = new VideoInfoPostQuery();
		// 给查询对象设置条件
		videoInfoPostQuery.setStatus(VideoStatusEnum.STATUS2.getStatus());
		videoInfoPostQuery.setVideoId(videoId);

		// 调用mapper的更新方法，根据条件（videoInfoPostQuery）更新视频信息（videoInfoPost），并获取更新影响的记录数
		Integer auditCount = this.videoInfoPostMapper.updateByParam(videoInfoPost, videoInfoPostQuery);

		// 如果更新影响的记录数为0，说明没有匹配到要更新的记录，审核失败，抛出业务异常
		if (auditCount == 0) {
			throw new BusinessException("审核失败，请稍后重试");
		}

		// 更新视频文件状态
		VideoInfoFilePost videoInfoFilePost = new VideoInfoFilePost();
		// 设置更新类型为“不更新”，通过枚举获取对应状态值
		videoInfoFilePost.setUpdateType(VideoFileUpdateTypeEnum.NO_UPDATE.getStatus());

		// 构建文件信息查询条件：通过 videoId 定位要更新的文件记录
		VideoInfoFilePostQuery filePostQuery = new VideoInfoFilePostQuery();
		filePostQuery.setVideoId(videoId);
		// 执行更新操作
		this.videoInfoFilePostMapper.updateByParam(videoInfoFilePost, filePostQuery);

		//状态判断：若视频状态是 STATUS4，直接返回，跳过后续逻辑
		if (VideoStatusEnum.STATUS4 == videoStatusEnum) {
			return;
		}

		// 查询视频主信息
		// 从 videoInfoPostMapper 查询上传的视频信息
		VideoInfoPost infoPost = this.videoInfoPostMapper.selectByVideoId(videoId);

		// 在正式的视频信息表查询信息，若视频不存在，则从 Redis 中获取系统配置
		VideoInfo dbVideoInfo = this.videoInfoMapper.selectByVideoId(videoId);
		if (dbVideoInfo == null) {
			// 从 Redis 获取系统配置
			SysSettingDto sysSettingDto = redisComponent.getSysSettingDto();
			// 标记待实现逻辑：给用户加硬币
			// TODO 给用户加硬币
		}

		// 同步视频信息到正式表
		// 将 VideoInfoPost表 转换为正式表实体 VideoInfo
		VideoInfo videoInfo = CopyTools.copy(infoPost, VideoInfo.class);
		// 执行插入或更新：若记录存在则更新，不存在则插入
		this.videoInfoMapper.insertOrUpdate(videoInfo);

		// 同步视频文件信息到正式表（先删后插）
		// 构建文件删除条件：通过 videoId 定位要删除的文件记录
		VideoInfoFileQuery videoInfoFileQuery = new VideoInfoFileQuery();
		videoInfoFileQuery.setVideoId(videoId);
		// 先删除正式表中该 videoId 的旧文件记录
		this.videoInfoFileMapper.deleteByParam(videoInfoFileQuery);

		// 重新插入最新文件信息到正式表
		// 构建文件查询条件：通过 videoId 从临时表查询文件记录
		VideoInfoFilePostQuery videoInfoFilePostQuery = new VideoInfoFilePostQuery();
		videoInfoFilePostQuery.setVideoId(videoId);
		// 从临时表查询文件列表
		List<VideoInfoFilePost> videoInfoFilePostList = this.videoInfoFilePostMapper.selectList(videoInfoFilePostQuery);

		// 将临时表文件记录转换为正式表实体列表
		List<VideoInfoFile> videoInfoFileList = CopyTools.copyList(videoInfoFilePostList, VideoInfoFile.class);
		// 批量插入正式表
		this.videoInfoFileMapper.insertBatch(videoInfoFileList);

		/**
		 * 删除临时文件
		 */
		// 从 Redis 获取待删除文件路径列表
		List<String> filePathList = redisComponent.getDelFileList(videoId);

        //遍历路径，执行文件删除
		if (filePathList != null) {
			for (String path : filePathList) {
				// 拼接完整文件路径（项目文件夹 + 固定目录 + 相对路径）
				File file = new File(appConfig.getProjectFolder() + Constants.FILE_FOLDER + path);

				// 文件存在则尝试删除
				if (file.exists()) {
					try {
						// 调用工具类删除目录/文件（支持递归删除）
						FileUtils.deleteDirectory(file);
					} catch (IOException e) {
						// 记录删除失败日志
						log.error("删除文件失败", e);
					}
				}
			}
		}

		//清空 Redis 中该 videoId 对应的待删除文件列表
		redisComponent.cleanDelFileList(videoId);

		// TODO 保存信息到es
	}

	/**
	 * 转码 TS
	 * @param completeVideo
	 */
	private void convertVideo2Ts(String completeVideo) {
		// 根据视频路径创建 File 对象，用于后续文件操作
		File videoFile = new File(completeVideo);
		// 获取视频文件所在的父文件夹（即视频所在目录）
		File tsFolder = videoFile.getParentFile();

		// 调用工具类方法，获取视频的编码格式（比如 HEVC、H.264 等）
		String codec = fFmpegUtils.getVideoCodec(completeVideo);

		// 判断视频编码是否为 HEVC（H.264）格式
		if (Constants.VIDEO_CODE_HEVC.equals(codec)) {
			// 拼接临时文件名（在原视频路径基础上，添加临时后缀 ）
			String tempFileName = completeVideo + Constants.VIDEO_CODE_TEMP_FILE_SUFFIX;
			// 将原视频文件重命名为临时文件（用于转码时避免覆盖原文件）
			new File(completeVideo).renameTo(new File(tempFileName));

			// 调用工具类方法，将 HEVC 编码的视频转为 H.264 编码（临时文件作为输入，输出到原视频路径）
			fFmpegUtils.convertHevc2Mp4(tempFileName, completeVideo);

			// 删除转码用的临时文件
			new File(tempFileName).delete();
		}

		// 调用工具类方法，执行视频转 TS 格式、生成 M3U8 索引和分片的逻辑（传入视频所在目录、原视频路径）
		fFmpegUtils.convertVideo2Ts(tsFolder, completeVideo);

		// 删除原视频文件（转码、切片完成后，原文件不再需要）
		videoFile.delete();
	}

	/**
	 * 合并分片文件的方法
	 *
	 * @param dirPath 源文件目录路径，包含所有分片文件
	 * @param toFilePath 目标文件路径，合并后的完整文件存放位置
	 * @param delSource 是否删除源分片文件的标志，true表示删除，false表示保留
	 */
	private void union(String dirPath, String toFilePath, Boolean delSource) {
		// 检查源目录是否存在
		File dir = new File(dirPath);
		if (!dir.exists()) {
			throw new BusinessException("目录不存在");
		}

		// 获取目录下所有分片文件
		File[] fileList = dir.listFiles();

		// 创建目标文件对象
		File targetFile = new File(toFilePath);

		// 使用RandomAccessFile以读写模式打开目标文件
		try (RandomAccessFile writeFile = new RandomAccessFile(targetFile, "rw")) {
			// 创建缓冲区用于文件读写
			byte[] b = new byte[1024 * 10]; // 10KB缓冲区

			// 按顺序遍历所有分片文件并合并
			for (int i = 0; i < fileList.length; i++) {
				int len = -1;

				// 构造分片文件路径（按照索引命名）
				File chunkFile = new File(dirPath + File.separator + i);
				RandomAccessFile readFile = null;

				try {
					// 以只读模式打开分片文件
					readFile = new RandomAccessFile(chunkFile, "r");

					// 循环读取分片文件内容并写入目标文件
					while ((len = readFile.read(b)) != -1) {
						writeFile.write(b, 0, len);
					}
				} catch (Exception e) {
					// 记录错误日志并抛出业务异常
					log.error("合并分片失败", e);
					throw new BusinessException("合并文件失败");
				} finally {
					// 确保读取文件流被关闭
					if (readFile != null) {
						readFile.close();
					}
				}
			}
		} catch (Exception e) {
			// 捕获IO异常并抛出业务异常
			throw new BusinessException("合并文件" + dirPath + "出错了");
		} finally {
			// 根据delSource标志决定是否删除源分片文件
			if (delSource && fileList != null) {
				for (int i = 0; i < fileList.length; i++) {
					fileList[i].delete();
				}
			}
		}
	}

	/*private void union(String dirPath, String toFilePath, Boolean delSource) {
		File dir = new File(dirPath);
		if (!dir.exists()) {
			throw new BusinessException("目录不存在");
		}
		File[] fileList = dir.listFiles();
		File targetFile = new File(toFilePath);
		try (RandomAccessFile writeFile = new RandomAccessFile(targetFile, "rw")) {
			byte[] b = new byte[1024 * 10];
			for (int i = 0; i < fileList.length; i++) {
				int len = -1;
				//创建读块文件的对象
				File chunkFile = new File(dirPath + File.separator + i);
				RandomAccessFile readFile = null;
				try {
					readFile = new RandomAccessFile(chunkFile, "r");
					while ((len = readFile.read(b)) != -1) {
						writeFile.write(b, 0, len);
					}
				} catch (Exception e) {
					log.error("合并分片失败", e);
					throw new BusinessException("合并文件失败");
				} finally {
					readFile.close();
				}
			}
		} catch (Exception e) {
			throw new BusinessException("合并文件" + dirPath + "出错了");
		} finally {
			if (delSource) {
				for (int i = 0; i < fileList.length; i++) {
					fileList[i].delete();
				}
			}
		}
	}*/

	/**
	 * 检查视频基本信息是否有变更
	 *
	 * @param videoInfoPost 新的视频信息
	 * @return Boolean 是否有变更
	 */
	private Boolean changeVideoInfo(VideoInfoPost videoInfoPost) {
		// 查询数据库中的视频信息
		VideoInfoPost dbInfo = this.videoInfoPostMapper.selectByVideoId(videoInfoPost.getVideoId());
		//标题，封面，标签，简介
		if (!videoInfoPost.getVideoName().equals(dbInfo.getVideoName())
				|| !videoInfoPost.getVideoCover().equals(dbInfo.getVideoCover())
				|| !videoInfoPost.getTags().equals(dbInfo.getTags())
				|| !videoInfoPost.getIntroduction().equals(dbInfo.getIntroduction())) {
			return true;
		} else {
			return false;
		}
	}
}